# -*- coding: utf-8 -*-
import sys
if sys.getdefaultencoding() != 'utf-8':
    reload(sys)
    sys.setdefaultencoding('utf-8')
import os, inspect
cwd = os.path.abspath(os.path.split(inspect.getfile( inspect.currentframe() ))[0])
sys.path[0:0] = [os.path.join(cwd, 'resources/lib')]

from xbmcswift2 import Plugin, xbmcplugin, xbmc
import os, re, xbmcswift2
from pprint import pprint
from traceback import print_exc
from xml.dom import minidom
import xml

STRINGS = {
}

plugin = Plugin()

log = plugin.log

def patch_the_plugin():
    import inspect
    def set_resolved_url(self, url=None, mimetype=None):
        if type(url) in [str, unicode]:
            item = xbmcswift2.ListItem(path=url)
        elif type(url) == dict:
            item = xbmcswift2.ListItem.from_dict(**url)
        else:
            item = xbmcswift2.ListItem(url)
        if mimetype:
            item.set_property('mimetype', mimetype)
        item.set_played(True)
        if not self._end_of_directory:
            xbmcplugin.setResolvedUrl(self.handle, url is not None, item.as_xbmc_listitem())
            # prevent from auto call endOfDirectory, which will not work because the handle already be erased by xbmc
            self._end_of_directory = True
            return [item]
        assert False, 'Already called endOfDirectory or setResolvedUrl.'
    Plugin.set_resolved_url = set_resolved_url

    if len(inspect.getargspec(Plugin.keyboard).args) == 4:
        def keyboard(self, default=None, heading=None, hidden=False, prefered_lang='en'):
            if heading is None:
                heading = self.addon.getAddonInfo('name')
            if default is None:
                default = ''
            keyboard = None
            if not hidden and prefered_lang.lower() in ['cn', 'zh-cn']:
                try:
                    import ChineseKeyboard
                    keyboard = ChineseKeyboard.Keyboard(default, heading)
                    keyboard.doModal()
                except:
                    pass
            if not keyboard:
                keyboard = xbmc.Keyboard(default, heading, hidden)
                keyboard.doModal()
            if keyboard.isConfirmed():
                return keyboard.getText()
        Plugin.keyboard = keyboard

    from xbmcswift2.urls import NotFoundException
    def _dispatch(self, path):
        for rule in self._routes:
            try:
                view_func, items = rule.match(path)
            except NotFoundException:
                continue
            log.info('Request for "%s" matches rule for function "%s"',
                     path, view_func.__name__)
            self.cur_func_name = view_func.__name__
            listitems = view_func(**items)
            self.cur_func_name = None

            # TODO: Verify the main UI handle is always 0, this check exists so
            #       we don't erroneously call endOfDirectory for alternate
            #       threads
            # Allow the returning of bare dictionaries so we can cache view
            if not self._end_of_directory and self.handle >= 0:
                if listitems is None:
                    listitems = self.finish(succeeded=False)
                else:
                    listitems = self.finish(listitems)

            return listitems
        raise NotFoundException, 'No matching view found for %s' % path
    Plugin._dispatch = _dispatch

    def get_storage_path(self, name='main'):
        return os.path.join(self.storage_path, name)
    Plugin.get_storage_path = get_storage_path

    from xbmcswift2.urls import UrlRule
    def __init__(self, url_rule, view_func, name, options):
        self._name = name
        self._url_rule = url_rule
        self._view_func = view_func
        self._options = options or {}
        self._keywords = re.findall(r'\<(.+?)\>', url_rule)
        
        #change <> to {} for use with str.format()
        self._url_format = self._url_rule.replace('<', '{').replace('>', '}')
        
        # Make a regex pattern for matching incoming URLs
        p = self._url_rule.replace('<', '(?P<').replace('>', '>[^/]*?)')
        if p[-1] == '/':
            # the tailing '/' sometimes removed by the xbmc, we should be able
            # to match the stripped url anyway.
            p += '?'
        
        try:
            self._regex = re.compile('^' + p + '$')
        except re.error, e:
            raise ValueError, ('There was a problem creating this URL rule. '
                               'Ensure you do not have any unpaired angle '
                               'brackets: "<" or ">"')
    UrlRule.__init__ = __init__

    def add_items(self, items):
        # For each item if it is not already a list item, we need to create one
        _items = []
        info_type = self.info_type if hasattr(self, 'info_type') else 'video'

        # Create ListItems for anything that is not already an instance of
        # ListItem
        for item in items:
            if not isinstance(item, xbmcswift2.ListItem):
                if 'info_type' not in item.keys():
                    item['info_type'] = info_type
                item = xbmcswift2.ListItem.from_dict(**item)
            _items.append(item)
            p, it, d = item.as_tuple()
            assert isinstance(p, basestring)
            xbmcplugin.addDirectoryItem(self.handle, p, it, d, len(items))

        # We need to keep track internally of added items so we can return them
        # all at the end for testing purposes
        self.added_items.extend(_items)

        # Possibly need an if statement if only for debug mode
        return _items
    Plugin.add_items = add_items

    @classmethod
    def from_dict(cls, label=None, label2=None, icon=None, thumbnail=None,
                  path=None, selected=None, info=None, properties=None,
                  context_menu=None, replace_context_menu=False, is_playable=None, info_type='video'):
        '''A ListItem constructor for setting a lot of properties not
        available in the regular __init__ method. Useful to collect all
        the properties in a dict and then use the **dct to call this
        method.
        '''
        listitem = cls(label, label2, icon, thumbnail, path)

        if selected is not None:
            listitem.select(selected)

        if info:
            listitem.set_info(info_type, info)

        if is_playable:
            listitem.set_is_playable(True)

        if properties:
            for key, val in properties:
                listitem.set_property(key, val)

        if context_menu:
            listitem.add_context_menu_items(context_menu, replace_context_menu)

        return listitem
    xbmcswift2.ListItem.from_dict = from_dict

    def open_url_in_sys_browser(self, url):
        os.system(str(r'uiopen "%s"' % url.split('|', 2)[0]))

    Plugin.open_url = open_url_in_sys_browser


patch_the_plugin()

@plugin.route('/open_url/<method>/<url>/', options = {'method': 'web_viewer'})
def open_url(url, method='web_viewer'):
    if method == 'sys':
        plugin.open_url(url)
        return False
    if method != 'web_viewer':
        return False
    try:
        from webviewer import webviewer
    except:
        print_exc()
        plugin.notify(_('Please install XBMC WebViewer addon first.'))
        return False
    webviewer.getWebResult(url.split('|', 2)[0], dialog=True)
    return True


advsetting_file = xbmc.translatePath("special://home/userdata/advancedsettings.xml")

log.info('adv setting file: %s', advsetting_file)

xmlDoc = None
if os.path.isfile(advsetting_file):
    try:
        xmlDoc = minidom.parse(advsetting_file)
    except:
        print_exc()
if xmlDoc is None:
    xmlDoc = minidom.Document()
    topNode = xmlDoc.createElement("advancedsettings")
    xmlDoc.appendChild(topNode)

topNode = xmlDoc.documentElement
if topNode.nodeName != 'advancedsettings':
    raise ValueError, 'invalid advancedsettings xml document'

print dir(topNode)
#pprint(topNode.childNodes)
print topNode.toxml()


def add_nav_item(items, navItem, insertPos=0):
    assert type(navItem) == dict
    navItem['is_playable'] = True
    navItem['info'] = { 'title': navItem['label'] }
    navItem['path'] = plugin.url_for('show_contextmenu')
    navItem['replace_context_menu'] = True
    context_menu_items = navItem.get('context_menu')
#    if context_menu_items:
#        del navItem['context_menu']
#    navItem = xbmcswift2.ListItem.from_dict(**navItem)
    if context_menu_items:
        context_menu_items.append(menu_show_settings)
        if xbmcswift2.CLI_MODE:
            items[insertPos:insertPos] = items_from_context_menu(context_menu_items)
#        navItem.add_context_menu_items(context_menu_items, True)
    items.insert(insertPos, navItem)

@plugin.route('/contextmenu/')
def show_contextmenu():
    ret = plugin.set_resolved_url()
    time.sleep(0.5)
    xbmc.executebuiltin('Action(ContextMenu)')
    return ret

def menu_item_goto(label, url, replace=False):
    return (
        label,
        'Container.Update(%s,%s)' % (str(url), str(replace)),
    )

def run_plugin_item(label, url):
    return (
        label,
        'RunPlugin(%s)' % str(url),
    )

def items_from_context_menu(context_menu_items):
    items = []
    CONTAINER_UPDATE_RE = re.compile('^Container.Update\((.*),(False|True)\)$')
    for title, action in context_menu_items:
        m = CONTAINER_UPDATE_RE.match(action)
        if not m:
            continue
        items.append({
            'label': title,
            'path': m.group(1),
        })
    return items


def getText(node):
    nodelist = node.childNodes
    rc = []
    for node in nodelist:
        if node.nodeType == node.TEXT_NODE:
            rc.append(node.data)
    return ''.join(rc)

def setText(node, text):
    t = minidom.Text()
    t.data = text
    if node.firstChild == None:
        node.appendChild(t)
    else:
        node.replaceChild(t,node.firstChild)

def isLeaf(node):
    for n in node.childNodes:
        if n.nodeType == node.ELEMENT_NODE:
            return False
    return True

def getNode(nodePath):
    parents = nodePath.split('.')
    node = xmlDoc.documentElement
    if not nodePath:
        parents = []
    for parent in parents:
        if not parent:
            break
        for n in node.childNodes:
            if n.nodeType == n.ELEMENT_NODE and n.nodeName == parent:
                node = n
                break
        else:
            log.info('node [%s] not found', parent)
            return
    print node.toxml()
    print node
    return node

def saveXml():
    print 'saveXml'
    c = xmlDoc.toxml()
    print c
    with open(advsetting_file, 'w') as f:
        f.write(c)
    print 'saveXml done.'

def parent_node(node_path):
    parents = node_path.split('.')
    return '.'.join(parents[:-1])

@plugin.route('/edit_value/<node_path>/')
def edit_node(node_path):
    node = getNode(node_path)
    input = plugin.keyboard(default=getText(node), heading=node_path)
    if input is not None:
        setText(node, input)
        print 'ret: [%s]' % node.toxml()
        saveXml()
        items = list_element(parent_node(node_path))
        return plugin.finish(items, succeeded=True, update_listing=True)

@plugin.route('/add_attribute/<node_path>/')
def add_attribute(node_path):
    print 'add_attribute(%s)' % node_path
    node = getNode(node_path)
    attrName = plugin.keyboard(heading=_('Input New Attribute Name'))
    if not attrName:
        return
    attrValue = plugin.keyboard(heading=_('Input Value for Attribute [{attrName}]').format(attrName=attrName))
    print 'user input attribute: [%s] = [%s]' % (attrName, attrValue)
#    attr = minidom.Attribute(attrName)
#    if attrValue is not None:
#        attr.nodeValue = attrValue
    node.setAttribute(attrName, attrValue)
    print 'attribute %s added' % attrName
    saveXml()
    xbmc.executebuiltin('Container.Refresh')

@plugin.route('/edit_attribute/<node_path>/<attr>')
def edit_attribute(node_path, attr):
    print 'edit_attribute(%s, %s)' % (node_path, attr)
    node = getNode(node_path)
    attrObj = node.attributes[attr]
    attrValue = plugin.keyboard(default=attrObj.nodeValue, heading=_('Input Value for Attribute [{attrName}]').format(attrName=attr))
    print 'user input attribute: [%s] = [%s]' % (attr, attrValue)
    if attrValue is not None:
        attrObj.nodeValue = attrValue
    print 'attribute %s edited' % attrObj
    saveXml()
    xbmc.executebuiltin('Container.Refresh')

@plugin.route('/del_attribute/<node_path>/<attr>')
def del_attribute(node_path, attr):
    print 'del_attribute(%s, %s)' % (node_path, attr)
    node = getNode(node_path)
    del node.attributes[attr]
    saveXml()
    xbmc.executebuiltin('Container.Refresh')

@plugin.route('/add_element/<parent>/', options = {'parent': ''})
def add_element(parent = ''):
    print 'add_element(%s)' % parent
    parentNode = getNode(parent)
    input = plugin.keyboard(heading=_('Input New Element Name'))
    print 'user input: [%s]' % input
    if input:
        parentNode.appendChild(minidom.Element(input))
        saveXml()
        xbmc.executebuiltin('Container.Refresh')

@plugin.route('/del_element/<node_path>/')
def del_element(node_path):
    print 'del_element(%s)' % node_path
    node = getNode(node_path)
    node.parentNode.removeChild(node)
    saveXml()
    xbmc.executebuiltin('Container.Refresh')

@plugin.route('/node_element/<parent>/', options = {'parent': ''})
def list_element(parent = ''):
    node = getNode(parent)
    items = []
    navItem = {
        'label': ('Current: [%s]' % parent) if parent else 'Current: [Root Level]',
        'context_menu': [
            run_plugin_item(
                _('Add Element'),
                plugin.url_for('add_element', parent=parent),
            ),
            run_plugin_item(
                _('Open Reference'),
                plugin.url_for('open_url', url='http://wiki.xbmc.org/index.php?title=Advancedsettings.xml'),
            ),
            run_plugin_item(
                _('Open Reference In System Browser'),
                plugin.url_for('open_url', url='http://wiki.xbmc.org/index.php?title=Advancedsettings.xml', method='sys'),
            ),
        ],
    }
    add_nav_item(items, navItem)
    for n in node.childNodes:
        if n.nodeType == n.ELEMENT_NODE:
            # print n.nodeName, n.nodeValue, getText(n)
            label = n.nodeName if not isLeaf(n) else '%s: [%s]' % (n.nodeName, getText(n))
            path = '.'.join([parent, n.nodeName])
            if path[0] == '.':
                path = path[1:]
            item = {
                'label': label,
                'path': plugin.url_for('list_element', parent=path) if not isLeaf(n) else plugin.url_for('edit_node', node_path=path),
                'context_menu': [
                    run_plugin_item(
                        _('Add Sub Element'),
                        plugin.url_for('add_element', parent=path),
                    ),
                    run_plugin_item(
                        _('Delete This Element'),
                        plugin.url_for('del_element', node_path=path),
                    ),
                    run_plugin_item(
                        _('Add Attribute'),
                        plugin.url_for('add_attribute', node_path=path),
                    ),
                ],
            }
            if n.attributes:
                for attrName, attrValue in n.attributes.items():
                    print '%s = %s' % (attrName, attrValue)
                    item['context_menu'].extend([
                        run_plugin_item(
                            _('Attr[{attrName}]: [{attrValue}]').format(attrName=attrName, attrValue=attrValue),
                            plugin.url_for('edit_attribute', node_path=path, attr=attrName),
                        ),
                        run_plugin_item(
                            _('Delete Attr [{attrName}]').format(attrName=attrName),
                            plugin.url_for('del_attribute', node_path=path, attr=attrName),
                        ),
                    ])
            items.append(item)
    return items

@plugin.route('/')
def index():
    return list_element()

def _(string_id):
    if type(string_id) == int:
        return plugin.get_string(string_id)
    elif string_id in STRINGS:
        return plugin.get_string(STRINGS[string_id])
    else:
        log.warning('String is missing: %s' % string_id)
        return string_id

@plugin.route('/settings')
def show_settings():
    log.info('show_settings()')
    plugin.open_settings()

menu_reload_page = (
    _('Reload Page'),
    'Container.Refresh',
)
menu_show_settings = (
    _('Advanced Editor Preference'),
    'RunPlugin(%s)' % plugin.url_for('show_settings'),
)

if __name__ == '__main__':
    plugin.run()
